Skip to content

fix: engine too many connections - #231

Merged
chriscarrollsmith merged 4 commits into
Promptly-Technologies-LLC:mainfrom
bhtabor:fix/engine_too_many_connections
Jul 29, 2026
Merged

fix: engine too many connections#231
chriscarrollsmith merged 4 commits into
Promptly-Technologies-LLC:mainfrom
bhtabor:fix/engine_too_many_connections

Conversation

@bhtabor

@bhtabor bhtabor commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Fix create_engine() connection leaks per request/call

bhtabor added 2 commits July 28, 2026 23:55
Fix create_engine() connection leaks per request/call
Use lru cached engine

@rafizamankhan rafizamankhan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhtabor Good catch. Caching a shared get_engine() stops per-request create_engine() leaks that can exhaust Postgres connections, and avoiding str(url) as a cache key is right (str(URL) masks the password as ***).

Follow-ups I added: pool_pre_ping (detect/replace dead connections on checkout), explicit pool caps, lifespan/tear_down_db cache clear, and a few core tests so the shared engine stays bounded and shuts down clean.

I dropped lru_cache. cache_clear() forgets entries but can’t dispose() the engines those entries held. Engines own connection pools; treating them like pure memoized values leaves pools open after “clear.”

Kept the URL-keyed dict for the same one-engine-per-URL reuse, with explicit dispose() on shutdown/teardown so it’s defensible for production and tests.

Cheers!

- Route rate_limit.py through the shared get_engine() cache instead of
  its own module-level singleton, so no second unmanaged pool survives
  clear_engine_cache() or goes stale when the connection URL changes.
- Guard the engine cache with a threading.Lock: sync dependencies run in
  FastAPI's threadpool, so concurrent first requests could race in
  get_engine() and orphan an undisposed engine, and clear_engine_cache()
  could hit a dict-mutated-during-iteration error.
- Remove the clear_engine_cache() side effect from tear_down_db(); test
  fixtures that relied on it now call it explicitly in their teardown.
- Make engine-cache tests isolation-safe via an engine_cache fixture so
  cleanup runs even when an assertion fails mid-test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chriscarrollsmith
chriscarrollsmith merged commit 246f969 into Promptly-Technologies-LLC:main Jul 29, 2026
1 check passed
chriscarrollsmith added a commit that referenced this pull request Jul 29, 2026
* chore: release v1.1.8 [skip ci]

Automatically generated by python-semantic-release

* fix: engine too many connections (#231)

* fix: engine too many connections

Fix create_engine() connection leaks per request/call

* refactor: use lru cached engine

Use lru cached engine

* add pool_pre_ping and dispose()

* fix: address review feedback on engine cache

- Route rate_limit.py through the shared get_engine() cache instead of
  its own module-level singleton, so no second unmanaged pool survives
  clear_engine_cache() or goes stale when the connection URL changes.
- Guard the engine cache with a threading.Lock: sync dependencies run in
  FastAPI's threadpool, so concurrent first requests could race in
  get_engine() and orphan an undisposed engine, and clear_engine_cache()
  could hit a dict-mutated-during-iteration error.
- Remove the clear_engine_cache() side effect from tear_down_db(); test
  fixtures that relied on it now call it explicitly in their teardown.
- Make engine-cache tests isolation-safe via an engine_cache fixture so
  cleanup runs even when an assertion fails mid-test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Rafiuzzaman Khan <rafiuzzamank@gmail.com>
Co-authored-by: chriscarrollsmith <chriscarrollsmith@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: github-actions <actions@users.noreply.github.com>
Co-authored-by: Biruk Haileye Tabor <biruk.haileye@gmail.com>
Co-authored-by: Rafiuzzaman Khan <rafiuzzamank@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Christopher Carroll Smith <chriscarrollsmith@users.noreply.github.com>
@bhtabor

bhtabor commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

I dropped lru_cache. cache_clear() forgets entries but can’t dispose() the engines those entries held. Engines own connection pools; treating them like pure memoized values leaves pools open after “clear.”

Excellent! Makes perfect sense. I was a bit reluctant to add lru_cache. I've a follow-up PR in case it helps get_user_from_request query.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants